package com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SufjanApplicationContext {

    private Class configClass;//配置文件类
    private ConcurrentHashMap<String,Object> singletonObjects=new ConcurrentHashMap<>();//单例池
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap=new ConcurrentHashMap<>();//bean定义池
    private List<BeanPostProcessor> beanPostProcessorList=new ArrayList<>();

    public SufjanApplicationContext(Class configClass) {
        this.configClass=configClass;

        //扫描所有bean类定义，添加到bean定义池
        scan(configClass);

        //为bean定义池中的定义为单例的bean，创建唯一bean并添加到单例池
        for (Map.Entry<String,BeanDefinition> entry:beanDefinitionMap.entrySet()) {
            String beanName= entry.getKey();
            BeanDefinition beanDefinition=entry.getValue();
            if (beanDefinition.getScope().equals("singleton")){
                Object bean=creatBean(beanName,beanDefinition);
                singletonObjects.put(beanName,bean);
            }
        }

    }

    private void scan(Class configClass) {
        // 解析ComponmentScan注解
        ComponmentScan componmentScan= (ComponmentScan) configClass.getDeclaredAnnotation(ComponmentScan.class);
        String path=componmentScan.value();//com.sufjan.service
        ClassLoader classLoader=SufjanApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path.replace(".", "/"));//file:/C:/Users/loc/IdeaProjects/_1208_myspringsource/out/production/_1208_myspringsource/com/sufjan/service
        File file=new File(resource.getFile());//C:\Users\loc\IdeaProjects\_1208_myspringsource\out\production\_1208_myspringsource\com\sufjan\service
        if (file.isDirectory()){
            File[] files=file.listFiles();
            for (File f:files) {
                String fileName = f.getAbsolutePath();//C:\Users\loc\IdeaProjects\_1208_myspringsource\out\production\_1208_myspringsource\com\sufjan\service\UserService.class
                if (fileName.endsWith(".class")){
                    String className=fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class")).replace("\\",".");//com.sufjan.service.UserService
                    try {
                        Class<?> clazz=classLoader.loadClass(className);
                        // 解析Component以获取Bean对象
                        if(clazz.isAnnotationPresent(Component.class)){
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                                beanPostProcessorList.add(instance);
                            }
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);//getDeclaredAnnotation与getAnnotation的区别在前者不包括父类继承的注解
                            String beanName=componentAnnotation.value();
                            BeanDefinition beanDefinition=new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            // 解析Scope注解
                            if (clazz.isAnnotationPresent(Scope.class)){
                                Scope scope=clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scope.value());
                            }else {
                                beanDefinition.setScope("singleton");
                            }
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException | NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    public Object getBean(String beanName) {
        if (beanDefinitionMap.containsKey(beanName)){
            BeanDefinition beanDefinition=beanDefinitionMap.get(beanName);
            Object bean;
            if (beanDefinition.getScope().equals("singleton")){
                bean=singletonObjects.get(beanName);
            }else {
                bean=creatBean(beanName,beanDefinition);
            }
            return bean;
        }
        return null;
    }

    private Object creatBean(String beanName,BeanDefinition beanDefinition) {
        Class clazz=beanDefinition.getClazz();
        try {
            Object instance=clazz.getDeclaredConstructor().newInstance();
            //依赖注入
            for (Field delaredField : clazz.getDeclaredFields()) {
                if (delaredField.isAnnotationPresent(AutoWired.class)){
                    Object bean=getBean(delaredField.getName());
                    if (bean==null){
                        //在这里抛出异常
                    }
                    delaredField.setAccessible(true);
                    delaredField.set(instance,bean);
                }
            }
            //Aware回调
            if (instance instanceof BeanNameAware){
                ((BeanNameAware)instance).setBeanName(beanName);
            }
            //初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance=beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }
            //初始化
            if (instance instanceof InitializingBean){
                System.out.println("初始化");
                try {
                    ((InitializingBean)instance).afterPropertiesSet();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //初始化后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance=beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
}
